package a日常研究._03java线程._02线程状态转换;

public class ThreadPractice {

    public static void main(String[] args) throws InterruptedException {
//        testBlockedState();
//        testWaitingState();
//        testStateAfterInvokeWait();
        testStateAfterInvokeJoin();
    }

    private static void testBlockedState() throws InterruptedException {
        Table table = new Table();
        Thread a = new Thread(table::use);
        Thread b = new Thread(table::use);
        a.start();
        Thread.sleep(1500);
        b.start();
        Thread.sleep(500);
        System.out.println(b.getState());
    }

    private static void testWaitingState() throws InterruptedException {
        Table table = new Table();
        Thread student = new Thread(() -> {
            try {
                table.use2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        student.start();
        Thread.sleep(1000);
        System.out.println(student.getState());// WAITING
        System.out.println("main->" + Thread.currentThread().getState());
    }
    static class Table {
        synchronized void use() {
            System.out.println(Thread.currentThread().getName() + "-使用桌子");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "就餐结束");
        }

        synchronized void use2() throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + "-使用桌子");
            // 忘记点餐了
            wait(100);
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "就餐结束");
        }
    }

    /**
     * 测试线程调用wait方法后的状态
     * 过程描述：
     * 1、t1开始启动，获取对象锁和cpu时间片；
     * 2、t2启动，先进入READY状态，但是获取不到对象锁，因为同步阻塞进入BLOCKED状态，并进入锁池；
     * 3、t1中执行wait方法，让出对象锁和cpu时间片，因为等待阻塞进入WAITING状态，进入等待队列等待notify；
     * 4、锁池中t2获取到了cpu时间片和对象锁，进入RUNNABLE状态;
     * 5、t2执行完毕后，调用对象锁notify方法，t1从等待队列进入锁池，WAITING → BLOCKED状态；
     * 6、t1获取对象锁和cpu时间片，BLOCKED → RUNNABLE状态，继续运行；
     */
    private static void testStateAfterInvokeWait() {
        // 锁对象
        Object lock = new Object();
        // 线程1，调用wait进入等待队列的线程
        final Thread[] t2pro = new Thread[1];
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    try {
                        if(i == 5) {
                            // 当i=5的时候，让出对象锁，t1进入等待队列
                            // 如果没人通知，t1一直等待，程序不会结束
                            if (t2pro[0] != null) {
                                System.out.println("t1运行时t2的状态->" + t2pro[0].getState());// BLOCKED
                            }
                            Thread.sleep(100);
                            lock.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 : running " + i);
                }
            }
        });
        t1.start();
        // 线程2，调用notify唤醒的线程
        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("t1在t2执行时的状态->" + t1.getState());// WAITING
                for (int i = 0; i < 10; i++) {
                    System.out.println("t2 : running " + i);
                }
                // 这里t2得到锁，执行完线程方法之后一定要通知t1停止等待。
                // 不然t1结束不了，处在一直等待通知的状态
                lock.notify();
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1在t2调用notify后的状态->" + t1.getState());
            }
        });
        t2pro[0] = t2;
        t2.start();
    }

    /**
     * 测试线程join后的状态
     * 过程描述：
     * 1、main线程中启动t1，t1先运行，当t1中i == 5时，启动t2，t3，此时二者都是RUNNABLE，等待调度；
     * 2、t2.join后，t1进入WAITING状态，不再争夺CPU，t2和t3进行CPU争夺，没成功获取CPU的进入BLOCKED状态；
     * 3、【可能发生】t2执行完成后，t2.join失效，t1重新变为RUNNABLE状态，t1和t3争夺CPU，t1争夺成功后调用t3.join，
     *    自己变成WAITING状态，t1争夺失败后变成BLOCKED状态，下面假设t1争夺成功（不管夺没夺成功都是t3运行，区别在与t3运行时的状态）；
     * 4、t3.join后，t1进入WAITING状态，不再争夺CPU，t3独占CPU进行任务，任务完成后，t1被唤醒并成为RUNNABLE状态；
     * 5、t1执行剩余工作，执行完毕后任务结束。
     */
    private static void testStateAfterInvokeJoin() {
        final Thread[] t2pro = new Thread[3];
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    if(i == 5) {
                        // 当i=5的时候，让t2，t3执行
                        if (t2pro[0] != null) {
                            System.out.println("t1在join前的状态->" + t2pro[0].getState());// RUNNABLE
                            // 注意这里顺序，如果穿插start-join的话就会变成串行了
                            t2pro[1].start();
                            t2pro[2].start();
                            System.out.println("t2在t2 join前的状态->" + t2pro[1].getState());// RUNNABLE
                            System.out.println("t3在t2 join前的状态->" + t2pro[1].getState());// RUNNABLE
                            t2pro[1].join();
                            System.out.println("！！t1在join中的状态->" + t2pro[0].getState());// 如果t2执行完成，RUNNABLE
                            t2pro[2].join();
                            System.out.println("t1在join后的状态->" + t2pro[0].getState());// RUNNABLE
                        }
                        Thread.sleep(100);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t1 : running " + i);
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("t2 : running " + i);
                System.out.println("t1在t2执行时的状态->" + t1.getState());// WAITING或BLOCkED
                System.out.println("t3在t2执行时的状态->" + t2pro[2].getState());// BLOCKED或TERMINATED
            }
        });
        Thread t3 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("t3 : running " + i);
                System.out.println("t1在t3执行时的状态->" + t1.getState());// WAITING或BLOCkED
                System.out.println("t2在t3执行时的状态->" + t2pro[1].getState());// BLOCKED或TERMINATED
            }
        });
        t2pro[0] = t1;
        t2pro[1] = t2;
        t2pro[2] = t3;
        t1.start();
    }

}
